package xcu.lxj.nettychat.websocket.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.Future;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @Author XinJian Lei
 * @Date 2024/9/15 16:23
 * @Version 1.0
 */


@Slf4j
@Import(NettyProperties.class)
@Configuration
@ConditionalOnProperty(prefix = "netty",name = "enabled",havingValue = "true" ,matchIfMissing = true)
public class NettyWebSocketServe {

    @Autowired
    NettyProperties nettyProperties;
    // 多个通道都用一个这个就行 因为声名了 @Sharable
    public static final WebSocketMessageHandle webSocketMessageHandle = new WebSocketMessageHandle();
    private  final NioEventLoopGroup bossGroup = new NioEventLoopGroup(10);
    private  final NioEventLoopGroup workerGroup = new NioEventLoopGroup(1000);

    /**
     * 启动 netty
     */
    @PostConstruct
    public void  start(){
        run();
        log.info("netty 服务器启动成功");
    }

    /**
     * 关闭的销毁方法
     */
    @PreDestroy
    public void destroy(){
        Future<?> future = bossGroup.shutdownGracefully();
        Future<?> future1 = workerGroup.shutdownGracefully();
        future1.syncUninterruptibly();
        future.syncUninterruptibly();
        log.info(" netty 服务 关闭完成");
    }

    /**
     *  netty 服务器的启动方法
     */
    public void run(){
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .option(ChannelOption.SO_KEEPALIVE,true)
                    .handler(new LoggingHandler(LogLevel.INFO) )
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // 添加心跳检测机制
                            pipeline.addLast(new IdleStateHandler(nettyProperties.getReaderIdleTimeSeconds(),nettyProperties.getWriterIdleTimeSeconds(),nettyProperties.getAllIdleTimeSeconds()));
                            // 添加http 解码器
                            pipeline.addLast(new HttpServerCodec());
                            //以块的方式写
                            pipeline.addLast(new ChunkedWriteHandler());
                            // 聚合 把http分段请求聚合起来
                            pipeline.addLast(new HttpObjectAggregator(8192));
                            // 添加http 处理器
                            pipeline.addLast(new HttpHeaderHandler());
                            //  WebSocketServerProtocolHandler 核心功能是把 http协议升级为 ws 协议，保持长连接
                            pipeline.addLast(new WebSocketServerProtocolHandler(nettyProperties.getWebSocketUri(),null,true));
                            // 处理 websocket 的消息
                            pipeline.addLast(webSocketMessageHandle);
                        }
                    });

            ChannelFuture future = serverBootstrap.bind(nettyProperties.getPort()).sync();
        } catch (InterruptedException e) {
            log.warn("netty服务器开启失败 错误原因{}" ,e.getMessage());
        }
    }


}
